﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SharePoint.Client;

namespace Sources
{
	/// <summary>
	/// Update the Artefacts in SharePoint
	/// </summary>
	public static class UpdateData
	{
		/// <summary>
		/// Update Fields in SharePoint
		/// </summary>
		/// <param name="context">SharePoint context</param>
		/// <param name="field">Fields informations</param>
		/// <returns>The SharePoint field instances</returns>
		public static Field UpdateField(ClientContext context, FieldArtefact field)
		{
			Field returnField = null;

			var web = field.ToRootWeb ? context.Site.RootWeb : context.Web;

			context.Load(web.Fields, fields => fields.Include(fld => fld.InternalName, fld => fld.SchemaXml));
			context.ExecuteQuery();

			foreach (var fld in web.Fields)
			{
				if (string.Equals(fld.InternalName, field.Name, StringComparison.CurrentCultureIgnoreCase))
				{
					returnField = fld;
					break;
				}
			}

			if (returnField == null)
			{
				// Handle not found
				return returnField;
			}

			if (field.Group != null) { returnField.Group = field.Group; }
			if (field.Required != null) { returnField.Required = (bool)field.Required; }
			
			if (field.ShowInNewForm != null) { returnField.SetShowInNewForm((bool)field.ShowInNewForm); }
			if (field.ShowInDisplayForm != null) { returnField.SetShowInDisplayForm((bool) field.ShowInDisplayForm); }
			if (field.ShowInEditForm != null) { returnField.SetShowInEditForm((bool) field.ShowInEditForm); }

			returnField.UpdateAndPushChanges(true);
			context.ExecuteQuery();

			UpdateTypedField(context, returnField, field);
			
			return returnField;
		}

		/// <summary>
		/// Update field Specific values
		/// </summary>
		/// <param name="context">SharePoint context</param>
		/// <param name="returnField">Field to upodate</param>
		/// <param name="field">Field informations</param>
		private static void UpdateTypedField(ClientContext context, Field returnField, FieldArtefact field)
		{
			switch (field.Type)
			{
				case FieldTypeEnum.Boolean:
					break;
				case FieldTypeEnum.Choice:
					var choiceField = returnField.TypedObject as FieldChoice;
					if (field.Choices != null) { choiceField.Choices = field.Choices; }
					if (field.Default != null) { choiceField.DefaultValue = field.Default; }
					choiceField.UpdateAndPushChanges(true);
					context.ExecuteQuery();
					break;
				//case FieldTypeEnum.DateTime:
				//	var dateField = returnField.TypedObject as FieldDateTime;
				//	dateField.UpdateAndPushChanges(true);
				//	context.ExecuteQuery();
				//	break;
				case FieldTypeEnum.Lookup:
					BindingManager.BindLookup(context, field, returnField);
					break;
				case FieldTypeEnum.LookupMulti:
					BindingManager.BindLookup(context, field, returnField);
					break;
				case FieldTypeEnum.Note:
					var noteField = returnField.TypedObject as FieldMultiLineText;
					noteField.RichText = (bool)field.RichText;
					// Textmode seten
					noteField.UpdateAndPushChanges(true);
					context.ExecuteQuery();
					break;
				case FieldTypeEnum.Number:
					var numberField = returnField.TypedObject as FieldNumber;
					numberField.MinimumValue = (int)field.Min;
					numberField.MaximumValue = (int) field.Max;
					numberField.UpdateAndPushChanges(true);
					context.ExecuteQuery();
					break;
				case FieldTypeEnum.Text:
					var textField = returnField.TypedObject as FieldText;
					//textField.MaxLength
					textField.UpdateAndPushChanges(true);
					context.ExecuteQuery();
					break;
				case FieldTypeEnum.URL:
					var urlField = returnField.TypedObject as FieldUrl;
					UrlFieldFormatType urlFormat = UrlFieldFormatType.Hyperlink;
					if (field.Format == FieldFormatEnum.Image)
						urlFormat = UrlFieldFormatType.Image;
					urlField.DisplayFormat = urlFormat;
					urlField.UpdateAndPushChanges(true);
					context.ExecuteQuery();
					break;
				case FieldTypeEnum.User:
					var userField = returnField.TypedObject as FieldUser;
					// userField.SelectionMode = 
					// Anzeigeformat
					userField.UpdateAndPushChanges(true);
					context.ExecuteQuery();
					break;
				case FieldTypeEnum.UserMulti:
					var userMultiField = returnField.TypedObject as FieldUser;
					// userField.SelectionMode = 
					// Anzeigeformat
					userMultiField.UpdateAndPushChanges(true);
					context.ExecuteQuery();
					break;
			}
		}

		/// <summary>
		/// Update ContentTypes in SharePoint
		/// </summary>
		/// <param name="context">SharePoint context</param>
		/// <param name="contentType">ContentTypes to deploy</param>
		/// <returns>The SharePoint ContentType instances</returns>
		public static ContentType UpdateContentType(ClientContext context, ContentTypeArtefact contentType)
		{
			ContentType returnContentType = null;
			var fields = new List<Field>();

			foreach (FieldArtefact fi in contentType.FieldItems)
			{
				fields.Add(UpdateField(context, fi));
			}

			context.Load(context.Web.ContentTypes, contentTypes => contentTypes.Include(ct => ct.Name, ct => ct.FieldLinks));
			context.ExecuteQuery();

			foreach (var ct in context.Web.ContentTypes)
			{
				if (string.Equals(ct.Name, contentType.Name, StringComparison.CurrentCultureIgnoreCase))
				{
					returnContentType = ct;
					break;
				}
			}

			if (returnContentType == null)
			{
				return returnContentType;
			}

			var creationInfo = new ContentTypeCreationInformation
			{
				Id = contentType.ID,
				Name = contentType.Name,
				Description = contentType.Description,
				Group = contentType.Group
			};
			returnContentType = context.Web.ContentTypes.Add(creationInfo);

			returnContentType.Name = contentType.Name;
			returnContentType.Description = contentType.Description;
			returnContentType.Group = contentType.Group;
			returnContentType.Update(true);

			context.Load(returnContentType, ct => ct.Name);
			context.ExecuteQuery();
			
			foreach (var fld in fields)
			{
				if (!returnContentType.FieldLinks.Any(fl => fl.Name == fld.InternalName))
				{
					var link = new FieldLinkCreationInformation
					{
						Field = fld
					};
					returnContentType.FieldLinks.Add(link);
					returnContentType.Update(true);
					context.ExecuteQuery();
				}
			}

			return returnContentType;
		}

		/// <summary>
		/// Update Lists in SharePoint
		/// </summary>
		/// <param name="context">SharePoint context</param>
		/// <param name="list">Lists to deploy</param>
		/// <returns>The SharePoint List instances</returns>
		public static List UpdateList(ClientContext context, ListArtefact list)
		{
			List returnList = null;

			context.Load(context.Web.Lists, lists => lists.Include(l => l.Title, l => l.ContentTypes));
			context.ExecuteQuery();

			foreach (var l in context.Web.Lists)
			{
				if (string.Equals(l.Title, list.Title, StringComparison.CurrentCultureIgnoreCase))
				{
					returnList = l;
					break;
				}
			}

			if (returnList == null)
			{
				return returnList;
			}

			returnList.Title = list.Title;
			returnList.Description = list.Description;
			returnList.Update();

			context.Load(returnList.ContentTypes, cts => cts.Include(ct => ct.Name));
			context.ExecuteQuery();

			if (list.ContentTypes != null)
			{
				foreach (var ct in list.ContentTypes)
				{
					if (!returnList.ContentTypes.Any(lct => lct.Name == ct.Name))
					{
						var contentType = UpdateContentType(context, ct);

						returnList.ContentTypes.AddExistingContentType(contentType);
					}
				}

				context.ExecuteQuery();

				if (list.RemoveOtherContentTypes)
				{
					for (var i = returnList.ContentTypes.Count - 1; i >= 0; i--)
					{
						if (list.ContentTypes.All(ct => ct.Name != returnList.ContentTypes[i].Name))
						{
							returnList.ContentTypes[i].DeleteObject();
						}
					}

					context.ExecuteQuery();
				}
			}

			return returnList;
		}
	}
}
